<?php
declare (strict_types = 1);

namespace app\command\gii;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\facade\Db;

/**
 * php think gii 应用名 表名(小驼峰) 模块名 -d 描述 -c 是否覆盖
 * php think gii admin icon '图标' -d '图标管理'  -c 1
 */
class Gii extends Command
{
	// 请求信息
	protected $title = '首页';	// 标题 采集
	protected $_isCover = false;// 是否覆盖
	protected $tableName;	// 表名- 下划线
	protected $fileName;	// 文件名-小驼峰(输入)
	protected $fileNameUp;	// 文件名-大驼峰(各种文件或者类名)
	protected $fileNameLow;	// 文件名-下划线(视图文件名等)
	protected $desc;		// 描述
	protected $userName;	// 作者
	protected $application;	// 应用名,如 admin 或者 api

	protected $date;
	protected $time;

	// 表字段信息
	public $tableFieldInfo = null;

	/**
	 * {@inheritdoc}
	 */
    protected function configure()
    {
        // 指令配置
		$this->setName('gii')
			->setDescription("Generate programs from tables, exec: php think gii admin icon '图标' -d '图标管理'  -c 1")

			//应用名
			->addArgument('application', Argument::REQUIRED, 'What is the application of the program?')

			//表名
			->addArgument('tableName', Argument::REQUIRED, 'What is the tableName of the program?')

			//标题
			->addArgument('title', Argument::REQUIRED, 'What is the title of the program?')

			//描述[选填]
			->addOption('desc', 'd', Option::VALUE_OPTIONAL, 'What is the desc of the program')

			//作者[选填]
			->addOption('userName', 'u', Option::VALUE_OPTIONAL, 'What is the userName of the program?')

			//是否覆盖[选填]
			->addOption('is_cover', 'c', Option::VALUE_OPTIONAL, 'is is_cover of the program?')

			->setHelp(sprintf('%suse gii Creates a new program%s', PHP_EOL, PHP_EOL));
    }

	/**
	 * Create the new program.
	 * @param Input $input
	 * @param Output $output
	 * @return int|void|null
	 */
	protected function execute(Input $input, Output $output)
	{
		$options = $input->getOptions();
		$arg = $input->getArguments();

		//验证参数
		if(empty($arg['application'])) return $this->_error('请提交应用名');
		if(empty($arg['tableName'])) return $this->_error('请提交表名');
		if(empty($arg['title'])) return $this->_error('请提交标题');

		$this->tableName = $this->_getLower($arg['tableName']);
		$this->fileName = $arg['tableName'];
		$this->fileNameUp = ucfirst((string)$arg['tableName']);
		$this->fileNameLow = $arg['tableName'];

		$this->title = $arg['title'];
		$this->desc = empty($options['desc']) ? '描述' : $options['desc'];
		$this->userName = empty($options['userName']) ? 'Gii' : $options['userName'];
		$this->application = $arg['application'];
		$this->_isCover = !empty($options['is_cover']);

		$this->date = date('Y/m/d');
		$this->time = date('H:i');


		try {
			//创建控制器
			$this->_createController();

			//创建自有模块
			$this->_createLogic();

			//创建自有模型
			$this->_createModel();

			//创建自有实体
			$this->_createEntity();

			//创建语言包
			$this->_createField();

			//创建列表视图
			$this->_createViewIndex();

			//创建表单视图
			$this->_createViewForm();
		} catch (\Exception $e) {
			$this->_error('error : ' . $e->getMessage());
			return ;
		}

		$output->writeln('<info>created</info> the program '. $this->application . '->'. $this->fileNameUp .' ok');
	}

	/**
	 * 创建表单视图
	 * @param $module
	 * @param $fileName
	 * @param $title
	 */
	private function _createViewForm() {
		$tableDesc = array_values($this->_getTableInfo($this->tableName));
		$fieldStr = '';
		unset($tableDesc[0]); //删除ID
		foreach($tableDesc as $field) {
			$fieldStr .= sprintf("echo %s->setRequire()->addVerify('required')->text('%s');\r\n        ",
				'$easyForm', $field['name']);
		}

		$data = [
			'{$application}'     => $this->application,
			'{$fileNameUp}'   => $this->fileNameUp,
			'{$fieldStr}'	=> $fieldStr,
		];

		//获取模板
		$stubPath = $this->_getStubPath('view_form');

		//获取目标文件
		$dir = app_path($this->application .'/view/' . $this->tableName);
		if(!realpath($dir)) mkdir($dir, 777, true);
		$filePath = $dir . 'form.php';
		$this->_createBase($stubPath, $data, $filePath);
	}

	/**
	 * 创建列表视图
	 * @param $module
	 * @param $fileName
	 * @param $title
	 */
	private function _createViewIndex() {
		$tableDesc = $this->_getTableInfo($this->tableName);
		$fieldsStr = '';
		foreach($tableDesc as $field) {
			$status = '';
			if($field['name'] == 'status') $status = ', toolbar:\'#toolStatus\'';
			$fieldsStr .= sprintf("{field:'%s', title:'<?php echo %s::%s ?>' %s},\r\n                ",
				$field['name'], $this->fileNameUp, $field['name'], $status);
		}

		$data = [
			'{$fileNameUp}' => $this->fileNameUp,
			'{$application}'=> $this->application,
			'{$fieldsStr}'	=> $fieldsStr,
		];

		//获取模板
		$stubPath = $this->_getStubPath('view_index');

		//获取目标文件
		$dir = app_path($this->application .'/view/' . $this->tableName);
		if(!realpath($dir)) mkdir($dir, 777, true);
		$filePath = $dir . 'index.php';
		$this->_createBase($stubPath, $data, $filePath);
	}

	public function _createField() {
		$fieldList = $this->_getTableInfo($this->tableName);
		$fieldStr = '';
		foreach($fieldList as $field) {
			$fieldStr .= sprintf("const %s = '%s';\r\n    ", $field['name'], $field['comment']);
		}

		$data = [
			'{$desc}'  => $this->desc,
			'{$user}'  => $this->userName,
			'{$date}'  => $this->date,
			'{$time}'  => $this->time,
			'{$application}'	=> $this->application,
			'{$fileNameUp}'		=> $this->fileNameUp,
			'{$fieldStr}'		=> $fieldStr,
		];

		//获取模板
		$stubPath = $this->_getStubPath('field');

		//获取目标文件
		$dir = app_path($this->application .'/model/field');
		if(!realpath($dir)) mkdir($dir, 777, true);
		$filePath = $dir . $this->fileNameUp . '.php';
		$this->_createBase($stubPath, $data, $filePath);
	}

	/**
	 * 创建自有实体
	 * @param $module
	 * @param $fileName
	 * @param string $desc
	 * @param string $userName
	 * @param string $date
	 * @param string $time
	 */
	private function _createEntity() {
        $fieldList = $this->_getTableInfo($this->tableName);
        $functionStr = '';
        foreach($fieldList as $field) {
            $name = $this->_getCamelize($field['name']);
            $functionStr .= sprintf("    // 获取%s\r\n    public function get%s() {\r\n        return %s->getData('%s');\r\n    }\r\n\r\n", $field['comment'], $name, '$this', $field['name']);
        }

        $data = [
            '{$desc}'  => $this->desc,
            '{$user}'  => $this->userName,
            '{$date}'  => $this->date,
            '{$time}'  => $this->time,
            '{$application}'	=> $this->application,
            '{$fileNameUp}'		=> $this->fileNameUp,
            '{$functionStr}'		=> $functionStr,
        ];

        //获取模板
        $stubPath = $this->_getStubPath('entity');

        //获取目标文件
        $dir = app_path($this->application .'/model/entity');
        if(!realpath($dir)) mkdir($dir, 777, true);
        $filePath = $dir . $this->fileNameUp . 'Entity.php';
        $this->_createBase($stubPath, $data, $filePath);
	}

	/**
	 * 创建自有模型
	 * @param $module
	 * @param $fileName
	 * @param string $desc
	 * @param string $userName
	 * @param string $date
	 * @param string $time
	 */
	private function _createModel() {
		$data = [
			'{$desc}'       => $this->desc,
			'{$userName}'   => $this->userName,
			'{$date}'       => $this->date,
			'{$time}'       => $this->time,
			'{$application}'     => $this->application,
			'{$fileNameUp}'   => $this->fileNameUp,
		];

		//获取模板
		$stubPath = $this->_getStubPath('model');

		//获取目标文件
		$dir = app_path($this->application .'/model/model');
		if(!realpath($dir)) mkdir($dir, 777, true);
		$filePath = $dir . $this->fileNameUp . '.php';
		$this->_createBase($stubPath, $data, $filePath);
	}

	/**
	 * 创建自有模块
	 * @param $module 模块名
	 * @param $fileName 文件名
	 * @param string $desc 描述
	 * @param string $userName 作者
	 * @param string $date 日期
	 * @param string $time 时间
	 */
	private function _createLogic() {
		$data = [
			'{$desc}'       => $this->desc,
			'{$userName}'   => $this->userName,
			'{$date}'       => $this->date,
			'{$time}'       => $this->time,
			'{$application}'     => $this->application,
			'{$fileNameUp}'   => $this->fileNameUp,
		];

		//获取模板
		$stubPath = $this->_getStubPath('logic');

		//获取目标文件
		$dir = app_path($this->application .'/logic');
		if(!realpath($dir)) mkdir($dir, 777, true);
		$filePath = $dir . $this->fileNameUp . 'Logic.php';
		$this->_createBase($stubPath, $data, $filePath);
	}

	/**
	 * 创建控制器
	 * @param $module 模块名
	 * @param $fileName 文件名
	 * @param $title 标题
	 * @param string $desc 描述
	 * @param string $userName 作者
	 * @param string $date 日期
	 * @param string $time 时间
	 */
	private function _createController() {
		$data = [
			'{$desc}'       => $this->desc,
			'{$userName}'   => $this->userName,
			'{$date}'       => $this->date,
			'{$time}'       => $this->time,
			'{$application}'     => $this->application,
			'{$fileNameUp}'   => $this->fileNameUp,
			'{$title}'      => $this->title
		];

		//获取模板
		$stubPath = $this->_getStubPath('controller');

		//获取目标文件
		$dir = app_path($this->application .'/controller');
		if(!realpath($dir)) mkdir($dir, 777, true);
		$filePath = $dir . $this->fileNameUp . 'Controller.php';

		$this->_createBase($stubPath, $data, $filePath);
	}

	private function _createBase($stubPath, $data, $filePath) {
		//获取数据
		$content = file_get_contents($stubPath);

		//整理变量
		$content = strtr($content, $data);

		//生成文件
		if(!$this->_isCover and is_file($filePath)) return true;
		file_put_contents($filePath, $content);
	}

	//获取模板文件
	private function _getStubPath($stubFileName) {
		return app_path('command/gii/stubs') . $stubFileName . '.stub';
	}

	//获取下划线
	private function _getLower($str) {
		return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . '_' . "$2", $str));
	}

    // 获取驼峰
    private function _getCamelize($str, $separator='_') {
        $str = $separator. str_replace($separator, " ", strtolower($str));
        return ucfirst(ltrim(str_replace(" ", "", ucwords($str)), $separator ));
    }

	private function _getTableInfo($fileName) {
		if(!$this->tableFieldInfo) {
			$this->tableFieldInfo = Db::name($fileName)->getFields();
		}
		return $this->tableFieldInfo;
	}

	private function _error($msg) {
		$this->output->writeln($msg);
		return false;
	}
}
